package com.news.service.impl;

import com.news.model.ArticleContent;
import com.news.model.ArticleImage;
import com.news.model.NewsItem;
import com.news.service.ContentExtractorService;
import com.news.util.HttpUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * 网页内容提取服务实现
 */
public class ContentExtractorServiceImpl implements ContentExtractorService {
    
    private static final Logger logger = LoggerFactory.getLogger(ContentExtractorServiceImpl.class);
    
    // 内容缓存
    private final Map<String, ArticleContent> contentCache = new ConcurrentHashMap<>();
    
    // 支持的图片格式
    private static final Set<String> SUPPORTED_IMAGE_FORMATS = new HashSet<>(
        Arrays.asList("jpg", "jpeg", "png", "gif", "webp", "svg", "bmp")
    );
    
    // 常见的正文内容选择器
    private static final String[] CONTENT_SELECTORS = {
        "article", ".article", "#article",
        ".content", "#content", ".post-content",
        ".entry-content", ".article-content", ".news-content",
        ".text", ".main-text", ".article-body",
        "main", ".main", "#main",
        ".story", ".story-body"
    };
    
    // 需要排除的元素选择器
    private static final String[] EXCLUDE_SELECTORS = {
        "script", "style", "nav", "header", "footer",
        ".advertisement", ".ad", ".ads", 
        ".sidebar", ".comments", ".comment",
        ".share", ".social", ".related"
    };
    
    @Override
    public ArticleContent extractContent(String url) {
        if (url == null || url.trim().isEmpty()) {
            return createErrorContent("URL为空", url);
        }
        
        // 清理和验证URL
        String cleanUrl = cleanAndValidateUrl(url);
        if (cleanUrl == null) {
            return createErrorContent("URL格式无效: " + url, url);
        }
        
        // 检查缓存
        if (contentCache.containsKey(cleanUrl)) {
            logger.debug("从缓存获取内容: {}", cleanUrl);
            return contentCache.get(cleanUrl);
        }
        
        try {
            logger.info("开始提取网页内容: {}", cleanUrl);
            
            // 获取网页HTML
            String html = HttpUtil.get(cleanUrl);
            Document doc = Jsoup.parse(html, cleanUrl);
            
            // 提取内容
            ArticleContent content = extractFromDocument(doc, cleanUrl);
            
            // 缓存结果
            if (content.isExtractSuccess()) {
                contentCache.put(cleanUrl, content);
                logger.info("内容提取成功: {} (文字长度: {}, 图片数量: {})", 
                    cleanUrl, content.getContentLength(), content.getImageCount());
            }
            
            return content;
            
        } catch (Exception e) {
            logger.error("提取网页内容失败: {}", cleanUrl, e);
            return createErrorContent("提取失败: " + e.getMessage(), cleanUrl);
        }
    }
    
    @Override
    public ArticleContent extractContent(NewsItem newsItem) {
        if (newsItem == null || newsItem.getUrl() == null) {
            return createErrorContent("新闻项或URL为空", null);
        }
        
        ArticleContent content = extractContent(newsItem.getUrl());
        
        // 补充新闻项的信息
        if (content.isExtractSuccess()) {
            if (content.getTitle() == null || content.getTitle().trim().isEmpty()) {
                content.setTitle(newsItem.getTitle());
            }
            if (content.getAuthor() == null || content.getAuthor().trim().isEmpty()) {
                content.setAuthor(newsItem.getAuthor());
            }
            if (content.getPublishTime() == null) {
                content.setPublishTime(newsItem.getPublishTime());
            }
        }
        
        return content;
    }
    
    @Override
    public boolean isSupported(String url) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        
        // 使用URL清理和验证功能
        String cleanUrl = cleanAndValidateUrl(url);
        if (cleanUrl == null) {
            logger.debug("URL验证失败，尝试基础检查: {}", url);
            // 如果严格验证失败，尝试基础检查
            return isBasicUrlSupported(url);
        }
        
        try {
            java.net.URL urlObj = new java.net.URL(cleanUrl);
            String host = urlObj.getHost().toLowerCase();
            
            // 支持大部分HTTP/HTTPS网站，但排除一些特殊类型
            boolean isHttp = cleanUrl.startsWith("http://") || cleanUrl.startsWith("https://");
            
            // 排除不支持的类型（更宽松的条件）
            boolean isUnsupported = host.equals("localhost") || 
                                  host.equals("127.0.0.1") ||
                                  cleanUrl.endsWith(".json") ||
                                  cleanUrl.endsWith(".xml") ||
                                  cleanUrl.endsWith(".pdf") ||
                                  cleanUrl.endsWith(".zip") ||
                                  cleanUrl.endsWith(".exe");
            
            boolean supported = isHttp && !isUnsupported;
            
            if (supported) {
                logger.debug("URL支持内容提取: {}", cleanUrl);
            } else {
                logger.debug("URL不支持内容提取: {} (isHttp: {}, isUnsupported: {})", cleanUrl, isHttp, isUnsupported);
            }
            
            return supported;
            
        } catch (Exception e) {
            logger.debug("URL支持性检查失败，尝试基础检查: {}, 错误: {}", cleanUrl, e.getMessage());
            // 如果出现异常，尝试基础检查
            return isBasicUrlSupported(url);
        }
    }
    
    /**
     * 基础URL支持性检查（更宽松的条件）
     */
    private boolean isBasicUrlSupported(String url) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        
        String lowerUrl = url.toLowerCase().trim();
        
        // 基本格式检查
        boolean hasProtocol = lowerUrl.startsWith("http://") || lowerUrl.startsWith("https://");
        
        // 排除明显不支持的格式
        boolean isUnsupported = lowerUrl.endsWith(".json") ||
                               lowerUrl.endsWith(".xml") ||
                               lowerUrl.endsWith(".pdf") ||
                               lowerUrl.endsWith(".zip") ||
                               lowerUrl.endsWith(".exe") ||
                               lowerUrl.endsWith(".mp4") ||
                               lowerUrl.endsWith(".mp3");
        
        // 包含常见域名或搜索参数的认为是支持的
        boolean hasCommonPattern = lowerUrl.contains("baidu.com") ||
                                 lowerUrl.contains("weibo.com") ||
                                 lowerUrl.contains("bilibili.com") ||
                                 lowerUrl.contains("sina.com") ||
                                 lowerUrl.contains("163.com") ||
                                 lowerUrl.contains("qq.com") ||
                                 lowerUrl.contains("people.com") ||
                                 lowerUrl.contains("xinhua") ||
                                 lowerUrl.contains("news") ||
                                 lowerUrl.contains("s?wd=") ||  // 百度搜索
                                 lowerUrl.contains("search");
        
        boolean supported = hasProtocol && !isUnsupported;
        
        // 如果有常见模式，即使格式稍有问题也支持
        if (hasCommonPattern && !isUnsupported) {
            supported = true;
        }
        
        logger.debug("基础URL检查: {} -> {} (hasProtocol: {}, hasCommonPattern: {}, isUnsupported: {})", 
                    url, supported, hasProtocol, hasCommonPattern, isUnsupported);
        
        return supported;
    }
    
    @Override
    public void clearCache() {
        contentCache.clear();
        logger.info("内容缓存已清空");
    }
    
    /**
     * 从Document提取内容
     */
    private ArticleContent extractFromDocument(Document doc, String url) {
        ArticleContent content = new ArticleContent();
        content.setSourceUrl(url);
        
        try {
            // 提取标题
            String title = extractTitle(doc);
            content.setTitle(title);
            
            // 提取作者
            String author = extractAuthor(doc);
            content.setAuthor(author);
            
            // 提取发布时间
            LocalDateTime publishTime = extractPublishTime(doc);
            content.setPublishTime(publishTime);
            
            // 提取正文内容
            String articleText = extractMainContent(doc);
            content.setContent(articleText);
            
            // 生成摘要
            String summary = generateSummary(articleText);
            content.setSummary(summary);
            
            // 提取图片
            List<ArticleImage> images = extractImages(doc, url);
            content.setImages(images);
            
            // 生成HTML内容
            String htmlContent = generateHtmlContent(content);
            content.setHtmlContent(htmlContent);
            
            content.setExtractSuccess(true);
            
        } catch (Exception e) {
            logger.error("从Document提取内容失败", e);
            content.setExtractSuccess(false);
            content.setErrorMessage("内容解析失败: " + e.getMessage());
        }
        
        return content;
    }
    
    /**
     * 提取标题
     */
    private String extractTitle(Document doc) {
        // 尝试多种标题选择器
        String[] titleSelectors = {
            "h1", ".title", ".article-title", ".post-title",
            "title", ".headline", ".news-title"
        };
        
        for (String selector : titleSelectors) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                String title = elements.first().text().trim();
                if (!title.isEmpty() && title.length() > 5) {
                    return title;
                }
            }
        }
        
        // 使用页面title作为后备
        Element titleElement = doc.selectFirst("title");
        if (titleElement != null) {
            return titleElement.text().trim();
        }
        
        return "无标题";
    }
    
    /**
     * 提取作者
     */
    private String extractAuthor(Document doc) {
        String[] authorSelectors = {
            ".author", ".byline", ".by-author", 
            "[rel=author]", ".writer", ".reporter"
        };
        
        for (String selector : authorSelectors) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                String author = elements.first().text().trim();
                if (!author.isEmpty()) {
                    return author;
                }
            }
        }
        
        return null;
    }
    
    /**
     * 提取发布时间
     */
    private LocalDateTime extractPublishTime(Document doc) {
        String[] timeSelectors = {
            "time", ".time", ".date", ".publish-time",
            ".article-time", ".post-date", "[datetime]"
        };
        
        for (String selector : timeSelectors) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                Element timeElement = elements.first();
                String datetime = timeElement.attr("datetime");
                if (datetime.isEmpty()) {
                    datetime = timeElement.text();
                }
                
                // 这里可以添加时间解析逻辑
                // 暂时返回当前时间
                if (!datetime.isEmpty()) {
                    return LocalDateTime.now();
                }
            }
        }
        
        return null;
    }
    
    /**
     * 提取主要内容
     */
    private String extractMainContent(Document doc) {
        // 移除不需要的元素
        for (String excludeSelector : EXCLUDE_SELECTORS) {
            doc.select(excludeSelector).remove();
        }
        
        // 尝试通过常见选择器找到正文
        for (String selector : CONTENT_SELECTORS) {
            Elements elements = doc.select(selector);
            if (!elements.isEmpty()) {
                Element contentElement = elements.first();
                String text = contentElement.text().trim();
                if (text.length() > 200) { // 内容长度合理
                    return cleanText(text);
                }
            }
        }
        
        // 后备方案：提取所有段落
        Elements paragraphs = doc.select("p");
        StringBuilder content = new StringBuilder();
        for (Element p : paragraphs) {
            String text = p.text().trim();
            if (text.length() > 20) { // 过滤太短的段落
                content.append(text).append("\n\n");
            }
        }
        
        return content.length() > 0 ? cleanText(content.toString()) : "无法提取内容";
    }
    
    /**
     * 提取图片
     */
    private List<ArticleImage> extractImages(Document doc, String baseUrl) {
        List<ArticleImage> images = new ArrayList<>();
        Elements imgElements = doc.select("img");
        
        for (Element img : imgElements) {
            try {
                String src = img.attr("src");
                if (src.isEmpty()) {
                    src = img.attr("data-src"); // 懒加载图片
                }
                
                if (!src.isEmpty()) {
                    // 转换为绝对URL
                    String absoluteUrl = img.absUrl("src");
                    if (absoluteUrl.isEmpty()) {
                        absoluteUrl = resolveUrl(baseUrl, src);
                    }
                    
                    if (isValidImageUrl(absoluteUrl)) {
                        ArticleImage image = new ArticleImage(absoluteUrl);
                        image.setAlt(img.attr("alt"));
                        image.setTitle(img.attr("title"));
                        
                        // 获取图片尺寸
                        String width = img.attr("width");
                        String height = img.attr("height");
                        try {
                            if (!width.isEmpty()) image.setWidth(Integer.parseInt(width));
                            if (!height.isEmpty()) image.setHeight(Integer.parseInt(height));
                        } catch (NumberFormatException ignored) {}
                        
                        images.add(image);
                    }
                }
            } catch (Exception e) {
                logger.warn("处理图片时出错", e);
            }
        }
        
        logger.info("提取到 {} 张图片", images.size());
        return images;
    }
    
    /**
     * 生成内容摘要
     */
    private String generateSummary(String content) {
        if (content == null || content.trim().isEmpty()) {
            return "";
        }
        
        String cleaned = content.replaceAll("\\s+", " ").trim();
        if (cleaned.length() <= 200) {
            return cleaned;
        }
        
        // 按句号分割，取前几句
        String[] sentences = cleaned.split("。");
        StringBuilder summary = new StringBuilder();
        int charCount = 0;
        
        for (String sentence : sentences) {
            if (charCount + sentence.length() > 180) {
                break;
            }
            if (summary.length() > 0) {
                summary.append("。");
            }
            summary.append(sentence.trim());
            charCount = summary.length();
        }
        
        return summary.toString() + (summary.toString().endsWith("。") ? "" : "...");
    }
    
    /**
     * 生成HTML内容用于显示
     */
    private String generateHtmlContent(ArticleContent content) {
        StringBuilder html = new StringBuilder();
        html.append("<html><body style='font-family: Microsoft YaHei, sans-serif; line-height: 1.6;'>");
        
        // 标题
        if (content.getTitle() != null) {
            html.append("<h2 style='color: #333;'>").append(content.getTitle()).append("</h2>");
        }
        
        // 作者和时间
        if (content.getAuthor() != null || content.getPublishTime() != null) {
            html.append("<p style='color: #666; font-size: 12px;'>");
            if (content.getAuthor() != null) {
                html.append("作者: ").append(content.getAuthor());
            }
            if (content.getPublishTime() != null) {
                if (content.getAuthor() != null) html.append(" | ");
                html.append("时间: ").append(content.getPublishTime());
            }
            html.append("</p>");
        }
        
        // 主图
        if (content.hasImages()) {
            ArticleImage mainImage = content.getMainImage();
            html.append("<p><img src='").append(mainImage.getUrl())
                .append("' style='max-width: 100%; height: auto;' alt='")
                .append(mainImage.getAlt() != null ? mainImage.getAlt() : "")
                .append("'></p>");
        }
        
        // 正文
        if (content.getContent() != null) {
            String[] paragraphs = content.getContent().split("\n\n");
            for (String para : paragraphs) {
                if (!para.trim().isEmpty()) {
                    html.append("<p>").append(para.trim()).append("</p>");
                }
            }
        }
        
        html.append("</body></html>");
        return html.toString();
    }
    
    /**
     * 清理文本
     */
    private String cleanText(String text) {
        return text.replaceAll("\\s+", " ")
                   .replaceAll("^\\s+|\\s+$", "")
                   .trim();
    }
    
    /**
     * 解析相对URL为绝对URL
     */
    private String resolveUrl(String baseUrl, String relativeUrl) {
        try {
            URL base = new URL(baseUrl);
            URL resolved = new URL(base, relativeUrl);
            return resolved.toString();
        } catch (Exception e) {
            return relativeUrl;
        }
    }
    
    /**
     * 检查是否为有效的图片URL
     */
    private boolean isValidImageUrl(String url) {
        if (url == null || url.trim().isEmpty()) {
            return false;
        }
        
        // 检查URL格式
        if (!url.startsWith("http://") && !url.startsWith("https://")) {
            return false;
        }
        
        // 检查文件扩展名
        String lowerUrl = url.toLowerCase();
        for (String format : SUPPORTED_IMAGE_FORMATS) {
            if (lowerUrl.contains("." + format)) {
                return true;
            }
        }
        
        // 一些图片服务可能没有明确的扩展名
        return lowerUrl.contains("image") || lowerUrl.contains("img") || lowerUrl.contains("photo");
    }
    
    /**
     * 清理和验证URL
     */
    private String cleanAndValidateUrl(String url) {
        if (url == null || url.trim().isEmpty()) {
            return null;
        }
        
        String cleanUrl = url.trim();
        
        // 检查是否有重复的协议（常见错误）
        if (cleanUrl.contains("http://http://") || cleanUrl.contains("https://https://")) {
            logger.warn("检测到重复协议，尝试修复: {}", cleanUrl);
            cleanUrl = cleanUrl.replaceFirst("^https?://", "");
            if (!cleanUrl.startsWith("http://") && !cleanUrl.startsWith("https://")) {
                cleanUrl = "https://" + cleanUrl;
            }
        }
        
        // 检查是否有重复的域名（如 www.baidu.comhttps://...）
        if (cleanUrl.matches(".*\\.(com|cn|org|net)https?://.*")) {
            logger.warn("检测到重复域名，尝试修复: {}", cleanUrl);
            // 找到第一个http的位置，从那里开始截取
            cleanUrl = cleanUrl.replace("https://www.baidu.comhttps://www.baidu.com", "https://www.baidu.com");
        }
        
        // 基本URL格式验证
        if (!cleanUrl.startsWith("http://") && !cleanUrl.startsWith("https://")) {
            cleanUrl = "https://" + cleanUrl;
        }
        
        try {
            // 验证URL格式
            java.net.URL urlObj = new java.net.URL(cleanUrl);
            String host = urlObj.getHost();
            
            // 检查主机名是否合法
            if (host == null || host.trim().isEmpty() || host.contains(" ")) {
                logger.warn("URL主机名无效: {}", cleanUrl);
                return null;
            }
            
            // 检查是否包含无效字符
            if (host.contains("http") || host.contains("://")) {
                logger.warn("主机名包含无效字符: {}", host);
                return null;
            }
            
            logger.debug("URL验证通过: {} -> {}", url, cleanUrl);
            return cleanUrl;
            
        } catch (Exception e) {
            logger.warn("URL格式验证失败: {}, 错误: {}", cleanUrl, e.getMessage());
            return null;
        }
    }
    
    /**
     * 创建错误内容
     */
    private ArticleContent createErrorContent(String errorMessage, String url) {
        ArticleContent content = new ArticleContent();
        content.setSourceUrl(url);
        content.setExtractSuccess(false);
        content.setErrorMessage(errorMessage);
        content.setTitle("内容提取失败");
        
        // 根据错误类型提供不同的错误信息和建议
        String friendlyMessage = generateFriendlyErrorMessage(errorMessage, url);
        content.setContent(friendlyMessage);
        
        return content;
    }
    
    /**
     * 生成友好的错误信息
     */
    private String generateFriendlyErrorMessage(String errorMessage, String url) {
        StringBuilder message = new StringBuilder();
        
        message.append("⚠️ 无法提取网页内容\n\n");
        
        if (errorMessage.contains("404")) {
            message.append("📄 错误类型：页面不存在 (404)\n");
            message.append("📝 说明：目标页面可能已被删除或链接已失效\n");
            message.append("💡 建议：\n");
            message.append("   • 检查链接是否正确\n");
            message.append("   • 尝试访问网站首页\n");
            message.append("   • 稍后重试或寻找其他相关资源\n");
        } else if (errorMessage.contains("UnknownHostException") || errorMessage.contains("DNS")) {
            message.append("🌐 错误类型：网络连接问题\n");
            message.append("📝 说明：无法解析网站域名或网络连接异常\n");
            message.append("💡 建议：\n");
            message.append("   • 检查网络连接\n");
            message.append("   • 确认网址拼写正确\n");
            message.append("   • 稍后重试\n");
        } else if (errorMessage.contains("timeout") || errorMessage.contains("超时")) {
            message.append("⏱️ 错误类型：连接超时\n");
            message.append("📝 说明：网站响应时间过长\n");
            message.append("💡 建议：\n");
            message.append("   • 稍后重试\n");
            message.append("   • 检查网络速度\n");
            message.append("   • 尝试访问其他页面\n");
        } else if (errorMessage.contains("URL格式无效")) {
            message.append("🔗 错误类型：链接格式错误\n");
            message.append("📝 说明：提供的网址格式不正确\n");
            message.append("💡 建议：\n");
            message.append("   • 检查网址是否完整\n");
            message.append("   • 确保以 http:// 或 https:// 开头\n");
            message.append("   • 检查是否有重复或多余的字符\n");
        } else if (errorMessage.contains("403")) {
            message.append("🚫 错误类型：访问被拒绝 (403)\n");
            message.append("📝 说明：网站拒绝了访问请求\n");
            message.append("💡 建议：\n");
            message.append("   • 网站可能需要登录\n");
            message.append("   • 可能存在访问限制\n");
            message.append("   • 尝试直接在浏览器中访问\n");
        } else {
            message.append("❓ 错误类型：未知错误\n");
            message.append("📝 说明：").append(errorMessage).append("\n");
            message.append("💡 建议：\n");
            message.append("   • 稍后重试\n");
            message.append("   • 检查网络连接\n");
            message.append("   • 尝试直接在浏览器中访问该链接\n");
        }
        
        if (url != null && !url.trim().isEmpty()) {
            message.append("\n🔗 原始链接：").append(url);
        }
        
        message.append("\n\n📞 如问题持续存在，请联系技术支持。");
        
        return message.toString();
    }
} 